﻿using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;
using System.IO.Packaging;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Xml;
using OpenLS.Core.Serialization;
using OpenLS.Spreadsheet.Extensibility;
using OpenLS.Spreadsheet.Office2003;

namespace OpenLS.Spreadsheet
{
    partial class Range
    {
        /// <summary>
        /// Saves the current range to a specified stream in a specified format
        /// </summary>
        /// <param name="stream">The stream</param>
        /// <param name="format">The format</param>
        public void Save (Stream stream, string format)
        {
            Contract.Requires(stream != null);
            Contract.Requires(format != null);
            Debug.WriteLine(string.Format("Saving {0}, format = {1}", this.Label, format));
            if (format == SpreadsheetDataFormats.SpreadsheetDocumentDataFormat)
            {
                SpreadsheetDocument doc = RangeOperations.RangeToWorkbook(this);
                Package package = Package.Open(stream, FileMode.Create, FileAccess.ReadWrite);
                doc.WritePackage(package);
                package.Close();
                return;
            }
            if (format == DataFormats.Rtf || format == DataFormats.Xaml || format == DataFormats.Text)
            {
                var document = ToFlowDocument("\t");
                Debug.WriteLine(string.Format("flow document = " + XamlWriter.Save(document)));
                RichTextBox richTextBox = new RichTextBox(document);
                richTextBox.SelectAll();
                var range = richTextBox.Selection;
                range.Save(stream, format);
                return;
            }
            if (format == DataFormats.CommaSeparatedValue)
            {
                var document = this.ToFlowDocument(",");
                var range = new TextRange(document.ContentStart, document.ContentEnd);
                range.Save(stream, DataFormats.Text);
                return;
            }
#if NET40
            var f = Enumerable.FirstOrDefault(FormatManager.DataFormats, ff => ff.ClipboardName == format);
            if (f != null)
            {
                f.Save(RangeOperations.RangeToWorkbook(this), stream);
                return;
            }
#endif
            throw new ArgumentException("format");
        }

        
        /// <summary>
        /// Load the current range from a specified stream in a specified format
        /// </summary>
        /// <param name="stream">The stream</param>
        /// <param name="format">The format</param>
        public void Load (Stream stream, string format)
        {
            if (format == SpreadsheetDataFormats.SpreadsheetDocumentDataFormat || format == SpreadsheetDataFormats.XmlSpreadsheetFormat)
            {
                var range = RangeFromStream(stream, format);
                if (range != null)
                {
                    using (Worksheet.CreateUndoScope(ApplicationCommands.Paste.Text))
                    {
                        using (this.Worksheet.Workbook.CreateCalculationDisabler())
                        {
                            var selection = PasteManager.GetPasteTarget(range, this);
                            Contract.Assert(selection.Worksheet == this.Worksheet);
                            selection.CopyFrom(range,
                                               new PasteInfo(RangeFunctions.ApplyValueAndFormulaAndStyleRecord, false,
                                                             false));
                        }
                    }
                    return;
                }
            }
            throw new ArgumentException("format");

        }

        internal static Range RangeFromStream (Stream stream, string format)
        {
            if (format == SpreadsheetDataFormats.SpreadsheetDocumentDataFormat)
            {
                Package p = Package.Open(stream);
                SpreadsheetDocument doc = new SpreadsheetDocument(SpreadsheetContext.Default);
                doc.ReadPackage(p);
                p.Close();
                Worksheet ws = doc.Workbook.View.ActiveSheet as Worksheet;
                return ws.Selection;
            }
            if (format == SpreadsheetDataFormats.XmlSpreadsheetFormat)
            {
#if DEBUG
                FileStream output = File.Open(@"c:\temp\output.xml", FileMode.Create);
                byte [] data = new byte[stream.Length];
                stream.Read(data, 0, data.Length);
                output.Write(data, 0, data.Length);
                output.Close();
                stream.Position = 0;
#endif
                using (XmlReader reader = new XmlTextReader(stream))
                {
                    using (
                        ReadContext ctx = new ReadContext(reader, null,
                                                          XmlSpreadsheetConstants.
                                                              XmlSpreadsheetNamespaceUri))
                    {
                        ExpandedRange r = new ExpandedRange();
                        ctx.AddData<ExpandedRange>(r);
                        ctx.AddData<StyleLookup>(new StyleLookup());
                        SpreadsheetDocument doc =
                            SpreadsheetDocument.CreateEmpty(SpreadsheetContext.Default);
                        //\\ new SpreadsheetDocument(SpreadsheetContext.Default); //\\
                        IOffice2003XmlSerializable w = doc.Workbook;
                        doc.Workbook.UndoEnabled = false;
                        w.ReadXmlSpreadsheet(ctx);
                        Worksheet ws =
                            (Worksheet) doc.Workbook.Sheets[doc.Workbook.Sheets.Count - 1];
                        ws.Selection =
                            ws.GetRange(new RangeAddress(new CellAddress(0, 0),
                                                         new CellAddress(
                                                             r.ExpandedRowCount - 1,
                                                             r.ExpandedColumnCount - 1)));
                        return ws.Selection;

                    }
                }
            }
#if NET40
            var f = Enumerable.FirstOrDefault(FormatManager.DataFormats, ff => ff.ClipboardName == format);
            if (f != null)
            {
#if DEBUG
                var testStream = File.Open(@"c:\temp\test.xls", FileMode.Create);
                byte[] bytes = new byte[stream.Length];
                stream.Read(bytes, 0, bytes.Length);
                testStream.Write(bytes, 0, bytes.Length);
                testStream.Close();
                stream.Position = 0;
#endif
                var doc = f.Open(stream);
                Worksheet worksheet = (Worksheet) doc.Workbook.Sheets[0];
                return worksheet.Selection;
            }
#endif

            return null;
        }

        public IRange GetArea(int areaIndex)
        {
            return this;
        }
    }
}